home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Shareware Grab Bag
/
Shareware Grab Bag.iso
/
007
/
a86b.arc
/
EFFADDR.DOC
< prev
next >
Wrap
Text File
|
1986-06-22
|
13KB
|
230 lines
---EFFADDR.DOC---
Effective Addresses
Most memory data accessing in the 86 family is accomplished via the mechanism of
the effective address. Wherever an effective address specifier "eb", "ew" or
"ed" appears in the list of 8086 instructions, you may use a wide variety of
actual operands in that instruction. These include general registers, memory
variables, and a variety of indexed memory quantities.
GENERAL REGISTERS: Wherever an "ew" appears, you can use any of the 16-bit
registers AX,BX,CX,DX,SI,DI,SP, or BP. Wherever an "eb" appears, you can
use any of the 8-bit registers AL,BL,CL,DL,AH,BH,CH, or DH. For example,
the "ADD ew,rw" form subsumes the 16-bit register-to-register adds; for
example, ADD AX,BX; ADD SI,BP; ADD SP,AX.
MEMORY VARIABLES: Wherever an "ew" appears, you can use a word-memory-variable.
Wherever an "eb" appears, you can use a byte-memory-variable. Variables are
typically declared in the DATA segment, using a DW declaration for a word-
variable, or a DB declaration for a byte-variable. For example, you can
declare variables:
DATA_PTR DW ?
ESC_CHAR DB ?
Later, you can load or store these variables:
MOV SI,DATA_PTR ; load DATA_PTR into SI for use
LODSW ; fetch the word pointed to by DATA_PTR
MOV DATA_PTR,SI ; store the value incremented by the LODSW
MOV BL,ESC_CHAR ; load the byte-variable ESC_CHAR
Alternatively, you can address specific unnamed memory locations by enclosing
the location value in square barckets; for example,
MOV AL,[02000] ; load contents of location 02000 into AL
Note that A86 discerned from context (loading into AL) that a BYTE at 02000
was intended. Sometimes this is impossible, and you must speicify byte or
word:
INC B[02000] ; increment the byte at location 02000
MOV W[02000],0 ; set the WORD at location 02000 to zero
INDEXED MEMORY: The 86 supports the use of certain registers as base pointers
and index registers into memory. BX and BP are the base registers; SI and DI
are the index registers. You may combine at most one base register, at most
one index register, and a constant number into a run-time pointer that
determines the location of the effective address memory to be used in the
instruction. These can be given explicitly, by enclosing the index registers
in brackets:
MOV AX,[BX]
MOV CX,W[SI+17]
MOV AX,[BX+BP+5]
MOV AX,[BX][BP]5 ; another way to write the same instruction
Or, indexing can be accomplished by declaring variables in a based structure:
STRUC [BP] ; NOTE based structures are unique to A86!
BP_SAVE DW ?
RET_ADDR DW ?
PARM1 DW ?
PARM2 DW ?
ENDS
INC PARM1 ; equivalent to INC W[BP+4]
Finally, indexing can be done by mixing explicit components with declared
ones:
TABLE DB 4,2,1,3,5
MOV AL,TABLE[BX]
Segmentation and Effective Addresses
The 86 family has four segment registers, CS, DS, ES, and SS, used to address
memory. Each segment register points to 64K bytes of memory within the 1-
megabyte memory space of the 86. (The start of the 64K is calculated by
multiplying the segment register value by 16; i.e., by shifting the value
left by one hex digit.) If your program's code, data and stack areas can
all fit in the same 64K bytes, you can leave all the segment registers set to
the same value. In that case, you won't have to think about segment registers--
no matter which one is used to address memory, you'll still get the same 64K.
If your program needs more than 64K, you must point one or more segment
registers to other parts of the memory space. In this case, you must take
care that your memory references use the segment registers you intended.
Each effective address memory access has a defualt segment register, to be used
if you do not explicitly specify which segment register you wish. For most
effective addresses, the default segment register is DS. The exceptions are
those effective addresses that use the BP register for indexing. All BP-indexed
memory references have a default of SS. (This is becuase BP is intended to be
used for addressing local variables, stored on the stack.)
If you wish your memory access to use a different segment register, you provide
a segment-override byte before the instruction containing the effective
address operand. In the A86 language, you code the override by giving the
name of the segment register you wish before the instruction mnemonic. For
example, suppose you want to load the AL register with the memory byte pointed
to by BX. If you code MOV AL,[BX], the DS register will be used to determine
which 64K segment BX is pointing to. If you want the byte to come from the
CS-segment instead, you code CS MOV AL,[BX]. Be aware that the segment-override
byte has effect only upon the single instruction that follows it. If you have
a sequence of instructions requiring overrides, you must give an override byte
before every instruction in the sequence. (In that case, you may wish to
consider changing the value of the default segment register for the duration of
the sequence.)
NOTE: This method for providing segment-overrides is unique to the A86
assembler! The assemblers provided by Intel and IBM (MS-DOS) attempt to figure
out segment allocation for you, and plug in segment-override bytes "behind your
back". In order to do this, those assemblers require you to inform them which
variables and structures are pointed to by which segment registers. That is
what the ASSUME directive in those assemblers is all about. I wrote Intel's
first 86 assembler, ASM86, so I have been watching the situation since day one.
Over the years, I have concluded that the ASSUME-mechanism creates far, far
more confusion that it solves. So I scrapped it; and the result is an assembler
with far less red tape. But if your program needs more than 64K, you do have
to manage those segment registers yourself; so take care!
Effective Use of Effective Addresses
Remember that all of the common instructions of the 86 family allow effective
addresses as operands. (The only major functions that don't are the AL/AX
specific ones: multiply, divide, and input/output). This means that you don't
have to funnel many through AL or AX just to do something with them. You can
perform all the common arithmetic, PUSH/POP, and MOVes from any general
register to any general register; from any memory location (indexed if you
like) to any register; and (this is most often overlooked) from any register TO
memory. The only thing you can't do in general is memory-to-memory. Among the
more common operations that inexperienced 86 programmers overlook are:
* setting memory variables to immediate values
* testing memory variables, and comparing them to constants
* preserving memory variables by PUSHing and POPping them
* incrementing and decrementing memory variables
* adding into memory variables
Encoding of Effective Addresses
Unless you are concerned with the nitty-gritty details of 86 instruction
encoding, you don't need to read this section.
Every instruction with an effective address has an encoded byte, known as the
effective address byte, following the 1-byte opcode for the instruction. (For
obscure reasons, Intel calls this byte the ModRM byte.) If the effective
address is a memory variable, or an indexed memory location with a non-zero
constant offset, then the effective address byte will be immediately followed
by the offset amount. Amounts in the range -128 to +127 are given by a single
signed byte, denoted by "D8" in the table below. Amounts requiring a 2-byte
representation are denoted by "D16" in the table below. As with all 16-bit
memory quantities in the 86 family, the word is stored with the least
significant byte FIRST.
The following table of effective address byte values is organized into 32
rows and 8 columns. The 32 rows give the possible values for the effective
address operand: 8 registers and 24 memory indexing modes. A 25th indexing
mode, [BP] with zero displacement, has been pre-empted by the simple-memory-
variable case. If you code [BP] with no displacement, you will get [BP]+D8,
with a D8-value of zero.
The 8 columns of the table reflect further information given by the effective
address byte. Usually, this is the identity of the other (always a register)
operand of a 2-operand instruction. Those instructions are identified by a
"/r" following the opcode byte in the instruction list. Sometimes, the
information given supplements the opcode byte in identifying the instruction
itself. Those instructions are identified by a "/" followed by a digit from
0 through 7. The digit tells which of the 8 columns you should use to find
the effective address byte.
For example, suppose you have a perverse wish to know the precise bytes
encoded by the instruction SUB B[BX+17],100. This instruction subtracts an
immediate quantity, 100, from an effective address quantity, B[BX+17]. By
consulting the instruction list, you find the general form SUB eb,ib. The
opcode bytes given there are 80 /5 ib. The "/5" denotes an effective-address
byte, whose value will be taken from column 5 of the table below. The offset
17 decimal, which is 11 hex, will fit in a single "D8" byte, so we take our
value from the "[BX] + D8" row. The table tells us that the effective
address byte is 6F. Immediately following the 6F is the offset, 11 hex.
Following that is the ib-value of 100 decimal, which is 64 hex. So the bytes
generated by SUB B[BX+17],100 are 80 6F 11 64.
Table of Effective Address byte values
s = ES CS SS DS
rb = AL CL DL BL AH CH DH BH
rw = AX CX DX BX SP BP SI DI
digit= 0 1 2 3 4 5 6 7
Effective
EA byte address:
values: 00 08 10 18 20 28 30 38 [BX + SI]
01 09 11 19 21 29 31 39 [BX + DI]
02 0A 12 1A 22 2A 32 3A [BP + SI]
03 0B 13 1B 23 2B 33 3B [BP + DI]
04 0C 14 1C 24 2C 34 3C [SI]
05 0D 15 1D 25 2D 35 3D [DI]
06 0E 16 1E 26 2E 36 3E D16 (simple var)
07 0F 17 1F 27 2F 37 3F [BX]
40 48 50 58 60 68 70 78 [BX + SI] + D8
41 49 51 59 61 69 71 79 [BX + DI] + D8
42 4A 52 5A 62 6A 72 7A [BP + SI] + D8
43 4B 53 5B 63 6B 73 7B [BP + DI] + D8
44 4C 54 5C 64 6C 74 7C [SI] + D8
45 4D 55 5D 65 6D 75 7D [DI] + D8
46 4E 56 5E 66 6E 76 7E [BP] + D8
47 4F 57 5F 67 6F 77 7F [BX] + D8
80 88 90 98 A0 A8 B0 B8 [BX + SI] + D16
81 89 91 99 A1 A9 B1 B9 [BX + DI] + D16
82 8A 92 9A A2 AA B2 BA [BP + SI] + D16
83 8B 93 9B A3 AB B3 BB [BP + DI] + D16
84 8C 94 9C A4 AC B4 BC [SI] + D16
85 8D 95 9D A5 AD B5 BD [DI] + D16
86 8E 96 9E A6 AE B6 BE [BP] + D16
87 8F 97 9F A7 AF B7 BF [BX] + D16
C0 C8 D0 D8 E0 E8 F0 F8 ew=AX eb=AL
C1 C9 D1 D9 E1 E9 F1 F9 ew=CX eb=CL
C2 CA D2 DA E2 EA F2 FA ew=DX eb=DL
C3 CB D3 DB E3 EB F3 FB ew=BX eb=BL
C4 CC D4 DC E4 EC F4 FC ew=SP eb=AH
C5 CD D5 DD E5 ED F5 FD ew=BP eb=CH
C6 CE D6 DE E6 EE F6 FE ew=SI eb=DH
C7 CF D7 DF E7 EF F7 FF ew=DI eb=BH
D8 denotes an 8-bit displacement following the EA byte,
to be sign-extended and added to the index.
D16 denotes a 16-bit displacement following the EA byte,
to be added to the index.
Default segment register is SS for effective addresses containing
a BP index; DS for other memory effective addresses.